package com.customer.dao;

import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

import javax.annotation.PostConstruct;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.customer.model.BasicEntity;

public  class BasicDao<T extends BasicEntity> extends HibernateDaoSupport implements IBasicDao<T>
{
	
	protected final Class<T> entityClass;
	
	@Autowired
	private SessionFactory sessionFactory;

	@PostConstruct
    public void initSessionFactory()
    {
    	this.setSessionFactory(sessionFactory);
    }
	

	@SuppressWarnings("unchecked")
	public BasicDao() 
	{
		try {
			entityClass = (Class<T>) ((ParameterizedType) getClass()
					.getGenericSuperclass()).getActualTypeArguments()[0];
		} 
		catch (Exception e) {
			throw new RuntimeException(getClass().getCanonicalName());
		}
	}
	
	@Override
	public int count(T t) {
		// TODO Auto-generated method stub
		return 0;
	}

	@SuppressWarnings("unchecked")
	@Override
	public int count(final DetachedCriteria detachedCriteria) 
	{
		return ((Number) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);
						return criteria.setProjection(Projections.rowCount())
								.uniqueResult();
					}
				})).intValue();
	}

	@Override
	public void exceuteSqlBatch(final String sql) {
		this.getHibernateTemplate().execute(new HibernateCallback<T>() {

			@Override
			public T doInHibernate(Session session) throws HibernateException,
					SQLException {
				session.createQuery(sql).executeUpdate();
				return null;
			}
		});
		
	}

	@Override
	public List<T> find(T t) {
		// TODO Auto-generated method stub
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(final DetachedCriteria detachedCriteria) {
		
		return (List<T>)this.getHibernateTemplate().executeFind(new HibernateCallback() {

			@Override
			public Object doInHibernate(Session session) throws HibernateException,
					SQLException
			{
				return  detachedCriteria.getExecutableCriteria(session).list();
			}
			
		});
	}

	@Override
	public List<T> findByPage(T t, int pageSize, int pageIndex) {
		// TODO Auto-generated method stub
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByPage(final DetachedCriteria detachedCriteria,final int pageSize,
		final int pageIndex) 
	{
	    return (List<T>)this.getHibernateTemplate().executeFind(new HibernateCallback() 
	    		{

					@Override
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException 
					{
						Criteria criteria = detachedCriteria
						.getExecutableCriteria(session);
						criteria.setFirstResult(pageSize * pageIndex);
						criteria.setMaxResults(pageSize);
						return criteria.list();
					}
	    	
		});
	}

	@Override
	public void merge(T t) {
		this.getHibernateTemplate().merge(t);
	}

	@Override
	public void remove(Integer id) {
		this.getHibernateTemplate().delete(load(id));
	}

	@Override
	public void save(T t) {
		this.getHibernateTemplate().save(t);
		
	}

	@Override
	public void saveBatch(final Collection<T> t) {
		this.getHibernateTemplate().execute(new HibernateCallback<T>() 
		{

			@Override
			public T doInHibernate(Session session) throws HibernateException,
					SQLException {
				int i = 0;
				for(T s:t)
				{
					if(i%20 == 0)
					{
						session.flush();
					}
					session.save(s);
					i++;
				}
				return null;
			}
			
		});
		
	}

	@Override
	public void update(T t) {
		this.getHibernateTemplate().saveOrUpdate(t);
		
	}

	@Override
	public void updateBatch(final Collection<T> t) {
		this.getHibernateTemplate().execute(new HibernateCallback<T>() 
				{

					@Override
					public T doInHibernate(Session session)
							throws HibernateException, SQLException {
						int i = 0;
						for(T s:t)
						{
							if(i%20 == 0)
							{
								session.flush();
							}
							session.saveOrUpdate(s);
							i++;
						}
						return null;
					}
			
		});
		
	}

	@Override
	public T load(Integer id) {
		return this.getHibernateTemplate().load(entityClass, id);
	}

}
